package Client;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.HeadlessException;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;

import javax.swing.Box;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import Client.excepciones.ActConnectException;
import Client.excepciones.ActLoginException;
import Client.excepciones.ActTransferException;
import Client.excepciones.IncorrectCodeException;
import Client.excepciones.NullParamIDSensorException;
import Client.excepciones.SensorNotExistsException;
import Client.excepciones.SensorOFFException;
import Client.excepciones.SensorONException;
import Client.excepciones.UnexpectedErrorException;
import Client.excepciones.UnknownSensorException;
import Client.excepciones.UserNotLoggedException;


/**
 * Clase panel para la ventana principal de la aplicacion, contiene los botones para operar sobre el tren-servidor.
 * @author inlacou
 *
 */
public class JPanelCliente extends JPanel{

	//Declaracion de variables.
	private Box pBotones;
	private JButton[] bts;
	public int nb;
	private JComboBox cbSensores;
	private DefaultListModel modelo;
	private Component[] componentes; 	//Este array es para guardar todos los componentes del papel, de modo que facilite el redibujado de todos ellos.
	public int nc;

	HashMap<Integer, Sensor> sensores;
	
	private JLabel lMensaje;
	private ClienteTCP cTCP;
	
	private JFrameCliente jf;

	/**
	 * Unico constructor de la clase JPanelCliente().
	 */
	public JPanelCliente(ClienteTCP clienteTCP, JFrameCliente jFrameClientePadre) {
		super();
		//Inicializacion de variables.
		pBotones = Box.createVerticalBox();
		modelo = new DefaultListModel();
		cTCP = clienteTCP;
		componentes = new Component[10];
		nc = 0;
		lMensaje = new JLabel("Conectese al servidor mediante ctrl+c.");
		bts = new JButton[10];
		nb = 0;

		jf = jFrameClientePadre;
		
		sensores = new HashMap<Integer, Sensor>();
		
		cbSensores = new JComboBox();
		cbSensores.setMaximumSize(new Dimension(120, 25));

		BorderLayout BL = new BorderLayout();
		this.setLayout(BL);

		//Listeners.
		ActionListener onGPS = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				try {
					if(cTCP.ONGPS()){
						lMensaje.setText("");
					}else{
						lMensaje.setText("El GPS ya esta encendido.");
					}
				} catch (IncorrectCodeException e) {
					lMensaje.setText("Respuesta incorrecta del servidor.");
				} catch (IOException e) {
					lMensaje.setText("Error en la conexion con servidor.");
				} catch (UserNotLoggedException e) {
					lMensaje.setText("Usuario desconectado.");
				} catch (UnexpectedErrorException e) {
					lMensaje.setText("Error inesperado.");
				}
			}
		};

		ActionListener offGPS = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				try {
					if(cTCP.OFFGPS()){
						lMensaje.setText("");
					}else{
						lMensaje.setText("El GPS ya esta apagado.");
					}
				} catch (IncorrectCodeException e) {
					lMensaje.setText("Respuesta incorrecta del servidor.");
				} catch (IOException e) {
					lMensaje.setText("Error en la conexion con servidor.");
				} catch (UserNotLoggedException e) {
					lMensaje.setText("Usuario desconectado.");
					jf.desconectar(); //TODO no probado aun.
				} catch (UnexpectedErrorException e) {
					lMensaje.setText("Error inesperado.");
				}
			}
		};

		ActionListener actualizar = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				try {
					if(cTCP.actualizar()){
						lMensaje.setText("Servidor actualizado.");
					}else{
						lMensaje.setText("Fallo en la actualizacion.");
					}
				} catch (IOException e) {
					lMensaje.setText("Error en la conexion con el servidor.");
				} catch (IncorrectCodeException e) {
					lMensaje.setText("Respuesta incorrecta del servidor.");
				} catch (ActTransferException e) {
					lMensaje.setText("Fallo transfiriendo la actualizacion.");
				} catch (ActLoginException e) {
					lMensaje.setText("Fallo en la autenticacion con el S. A.");
				} catch (UserNotLoggedException e) {
					lMensaje.setText("Usuario desconectado.");
					jf.desconectar(); //TODO no probado aun.
				} catch (ActConnectException e) {
					lMensaje.setText("Conexion con el S.A. fallida.");
				} catch (UnexpectedErrorException e) {
					lMensaje.setText("Error inesperado.");
				}
			}
		};

		ActionListener getCoord = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				try {
					JOptionPane.showMessageDialog(jf, "La posicion actual del Tren es: \n" + cTCP.getLoc());
				} catch (HeadlessException e) {
					lMensaje.setText("?¿."); //TODO
				} catch (IOException e) {
					lMensaje.setText("Error en la conexion con el servidor.");
				} catch (IncorrectCodeException e) {
					lMensaje.setText("Respuesta incorrecta del servidor.");
				} catch (UserNotLoggedException e) {
					lMensaje.setText("Usuario desconectado.");
					jf.desconectar(); //TODO no probado aun.
				} catch (UnexpectedErrorException e) {
					lMensaje.setText("Error inesperado.");
				}
			}
		};
		
		ActionListener listaSensores = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				try {
					setSensores(cTCP.listaSensores());
					getNombresSensores();
					lMensaje.setText("");
				} catch (IncorrectCodeException e) {
					lMensaje.setText("Respuesta incorrecta del servidor.");
				} catch (IOException e) {
					lMensaje.setText("Error en la conexion con el servidor.");
				} catch (UserNotLoggedException e) {
					lMensaje.setText("Usuario desconectado.");
					jf.desconectar(); //TODO no probado aun.
				} catch (UnexpectedErrorException e) {
					lMensaje.setText("Error inesperado.");
				}
			}
		};

		ActionListener onSensor = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
					try {
						cTCP.ON(cbSensores.getSelectedIndex()+1);
						lMensaje.setText("");
					} catch (IOException e) {
						lMensaje.setText("Error en la conexion con el servidor.");
					} catch (SensorNotExistsException e) {
						lMensaje.setText("El sensor elegido no existe.");
					} catch (SensorONException e) {
						lMensaje.setText("El sensor ya se encuentra en estado ON.");
					} catch (IncorrectCodeException e) {
						lMensaje.setText("Respuesta incorrecta del servidor.");
					} catch (UserNotLoggedException e) {
						lMensaje.setText("Usuario desconectado.");
						jf.desconectar(); //TODO no probado aun.
					} catch (UnexpectedErrorException e) {
						lMensaje.setText("Error inesperado.");
					}
			}
		};
		
		ActionListener offSensor = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				try {
					cTCP.OFF(cbSensores.getSelectedIndex()+1);
					lMensaje.setText("");
				} catch (IOException e) {
					lMensaje.setText("Error en la conexion con el servidor.");
				} catch (SensorNotExistsException e) {
					lMensaje.setText("El sensor elegido no existe.");
				} catch (SensorOFFException e) {
					lMensaje.setText("El sensor ya se encuentra en estado OFF.");
				} catch (IncorrectCodeException e) {
					lMensaje.setText("Respuesta incorrecta del servidor.");
				} catch (UserNotLoggedException e) {
					lMensaje.setText("Usuario desconectado.");
					jf.desconectar(); //TODO no probado aun.
				} catch (UnexpectedErrorException e) {
					lMensaje.setText("Error inesperado.");
				}
			}
		};
		
		ActionListener getValorSensor = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
				try {
					String s = cTCP.getValAct(cbSensores.getSelectedIndex()+1);
					s = s.substring(2);
					StringTokenizer st = new StringTokenizer(s,";");
					JOptionPane.showMessageDialog(jf, "El valor del " + cbSensores.getSelectedItem() + " es: \n" + 
							"Fecha: " + st.nextToken() + "\n" +
							"Hora: " + st.nextToken() + "\n" +
							"Localizacion: " + st.nextToken() + "\n" +
							"Valor: " + st.nextToken() + "\n");
					lMensaje.setText("");
				} catch (IOException e) {
					lMensaje.setText("Error en la conexion con el servidor.");
				} catch (UnknownSensorException e) {
					lMensaje.setText("Sensor desconocido.");
				} catch (SensorOFFException e) {
					lMensaje.setText("El sensor se encuentra en estado OFF.");
				} catch (IncorrectCodeException e) {
					lMensaje.setText("Respuesta incorrecta del servidor.");
				} catch (NullParamIDSensorException e) {
					lMensaje.setText("Sensor nulo.");
				} catch (UserNotLoggedException e) {
					lMensaje.setText("Usuario desconectado.");
					jf.desconectar(); //TODO no probado aun.
				} catch (UnexpectedErrorException e) {
					lMensaje.setText("Error inesperado.");
				}
			}
		};
		
		ActionListener historico = new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent arg0) {
					try {
						JDialogHistorico jdH = new JDialogHistorico(jf, ModalityType.APPLICATION_MODAL, cTCP.historico(cbSensores.getSelectedIndex()+1));
						jdH.setVisible(true);
						lMensaje.setText("");
					} catch (IOException e) {
						lMensaje.setText("Error en la conexion con el servidor.");
					}  catch (IncorrectCodeException e) {
						lMensaje.setText("Respuesta incorrecta del servidor.");
					} catch (UserNotLoggedException e) {
						lMensaje.setText("Usuario desconectado.");
						jf.desconectar(); //TODO no probado aun.
					} catch (UnexpectedErrorException e) {
						lMensaje.setText("Error inesperado.");
					}
			}
		};

		//Insercion de componentes.
		pBotones.add(Box.createVerticalStrut(10));
		addButton("Obtener lista de sensores", pBotones, listaSensores);
		pBotones.add(Box.createVerticalStrut(10));
		pBotones.add(cbSensores);
		addComponent(cbSensores);
		addButton("ON", pBotones, onSensor);
		addButton("OFF", pBotones, offSensor);
		addButton("Obtener valor de sensor", pBotones, getValorSensor);
		addButton("Obtener lista de medidas", pBotones, historico);
		pBotones.add(Box.createVerticalStrut(10));
		addButton("GPS ON", pBotones, onGPS);
		addButton("GPS OFF", pBotones, offGPS);
		addButton("Obtener coordenadas GPS", pBotones, getCoord);
		pBotones.add(Box.createVerticalStrut(10));
		addButton("Actualizar servidor", pBotones, actualizar);
		pBotones.add(Box.createVerticalStrut(10));
		this.add(pBotones, BorderLayout.CENTER);
		this.add(lMensaje, BorderLayout.SOUTH);

	}

	/**
	 * Metodo que anyade un boton con texto obtenido por parametro al contenedor pasado como parametro.
	 * @param text
	 * @param container
	 */
	private void addButton(String text, Container container) {
		JButton button = new JButton(text);
		button.setAlignmentX(Component.CENTER_ALIGNMENT);
		container.add(button);
		bts[nb] = button;
		addComponent(button);
		nb++;
	}

	private void addButton(String text, Container container, ActionListener al) {
		JButton button = new JButton(text);
		button.setAlignmentX(Component.CENTER_ALIGNMENT);
		button.addActionListener(al);
		container.add(button);
		bts[nb] = button;
		addComponent(button);
		nb++;
	}

	/**
	 * Funcion que anade un componente obtenido por parametro al array de componentes "componentes".
	 * @param c
	 */
	private void addComponent(Component c){
		componentes[nc] = c;
		nc++;
	}

	/**
	 * Metodo que cambia el estado "enabled" de todos los botones del panel, a el estado introducido como parametro.
	 */
	public void setButtonsEnabled(boolean b){
		for(int i = 0; i < nb; i++){
			bts[i].setEnabled(b);
		}
	}

	/**
	 * Metodo que cambia el estado "enabled" de los componentes de este JPanel a true o false, segun el parametro recibido.
	 * @param b
	 */
	public void setComponentsEnabled(boolean b){
		for(int i = 0; i < nc; i++){
			if((componentes[i] instanceof JComboBox) && b){
				((JComboBox)componentes[i]).removeAllItems();
			}
			componentes[i].setEnabled(b);
		}
	}

	/**
	 * Metodo que cambia el estado "enabled" del boton en la posicion introducida como parametro.
	 * @param b
	 * @param posb
	 */
	public void setButtonEnabled(boolean b, int posb){
		bts[posb].setEnabled(b);
	}

	/**
	 * Metodo que devuelve el boton en la posicion introducida como parametro.
	 * @param pos
	 * @return
	 */
	public JButton getButton(int pos){
		return bts[pos];
	}

	/**
	 * Metodo que determina la variable ClienteTCP.
	 * @param clienteTCP
	 */
	public void setTCP(ClienteTCP clienteTCP){
		cTCP = clienteTCP;
	}

	/**
	 * Metodo que altera el mensaje del label utilizado para informar al usuario.
	 * @param mensaje
	 */
	public void setMensaje(String mensaje){
		lMensaje.setText(mensaje);
	}
	
	/**
	 * Metodo que carga los sensores, recibiendo sus datos como parametro.
	 * @param a
	 */
	public void setSensores(ArrayList<String[]> a){
		Sensor sensor;
		for(int i = 0; i < a.size(); i++){
			String[] s = a.get(i);
			sensor = new Sensor(s[0], s[1], s[2]);
			sensores.put(i, sensor);
		}
	}
	
	/**
	 * Metodo que coloca los nombres de los sensores en el comboBox, tras limpiarlo de valores anteriores.
	 */
	public void getNombresSensores(){
		cbSensores.removeAllItems();
		for(int i = 0; i < sensores.size(); i++){
			cbSensores.addItem(sensores.get(i).getNombre());
		}
	}
	
	/**
	 * Sobreescribe el metodo repaint, de modo que se redibujen todos los objetos
	 * del panel con las fuentes, color y tama�o de letra del componente padre de la aplicacion.
	 */
	public void repaint(){
		try{ 
			//La primera vez que se dibuja todo (cuando se inicia la aplicacion),
			//los metodos a continuacion generan una excepcion de tipo NullPointerException 
			//dado que no puede obtener los datos. En cambio, una vez dibujado todo, en el 
			//redibujado (de ahi "repaint") si obtiene los parametros y funciona con normalidad.
			this.setFont(this.getParent().getParent().getFont());
			this.setForeground(this.getParent().getParent().getForeground());
			this.setBackground(this.getParent().getParent().getBackground());
		}catch(NullPointerException npe){
			//Dado que solo generara esta excepcion una vez, simplemente queremos que la ignore,
			//de modo que no hacemos nada.
		}
		//Cambiamos las caracteristicas de los componentes de este JPanel, que estaran en el array "componentes"
		for(int i = 0; i < nc; i++){
			componentes[i].setFont(this.getFont());
			componentes[i].setForeground(this.getForeground());
			if(componentes[i] instanceof JList){
				((JList)componentes[i]).setModel(modelo);
			}
			componentes[i].repaint();
		}
	}


}